<?php

namespace Buu\Modules\RedisSetting\Controllers;

use App\Admin\Extensions\Tools\UserButton;
use Encore\Admin\Form;
use Encore\Admin\Grid;
use App\Models\DbTables;
use App\Models\DbInstance;
use App\Models\RedisDbLog;
use Doraemon\tools\HhzEnv;
use App\Models\RedisBusLine;
use hhz\constant\basic\businessLine;
use hhz\constant\basic\redisType;
use hhz\constant\basic\useScene;
use Illuminate\Http\Request;
use Encore\Admin\Widgets\Tab;
use App\Models\RedisInstance;
use ConfigServices\Constants;
use App\Models\RedisKeySetting;
use Encore\Admin\Facades\Admin;
use Encore\Admin\Layout\Content;
use App\Models\RedisCounterColumn;
use Illuminate\Support\MessageBag;
use ConfigServices\ConfigServices;
use Encore\Admin\Controllers\AdminController;

/**
 * description
 *
 * @category   Module name
 * @package    PSR
 * @subpackage Documentation\API
 * @author     weiyizheng
 * @date       2022/12/6
 * @time       4:22 下午
 */
class RedisKeySettingController extends AdminController
{
    const SEP = ":";
    const XHX = "_";
    public $title = 'Redis Key配置管理';

    public function index(Content $content)
    {
        $tab = new Tab();
        $tab->add($this->title, $this->grid(), true);
        $tab->addLink('业务线实例关系', '/admin/RedisSetting/redis-bus-line');
        return $content
            ->title($this->title)
            ->description()
            ->breadcrumb(['text' => 'Redis Key配置管理'])
            ->row($tab);
    }

    public function grid()
    {
        $grid = new Grid(new RedisKeySetting());
        Admin::script($this->deleteScript());
        $grid->paginate(10);
        $grid->disableExport();
        $grid->disableBatchActions();
        $grid->model()->where('is_delete', '=', 0);
        $grid->column('id', 'ID')->sortable();
//        $grid->column('b_l_id', 'redis实例')->display(function () {
//            $redisBusLine = RedisBusLine::query()->find($this->b_l_id);
//            return RedisInstance::query()->find($redisBusLine['r_i_id'])->name;
//        })->label();
        $grid->column('key_name', 'key')->display(function () {
            return $this->bus_line . self::SEP . $this->scene . self::SEP . $this->key_name;
        });

//        $grid->column('const_key', 'const_key');
        $grid->column('desc', '描述');
        $grid->column('bus_line', '业务线-场景')->display(function () {
            return businessLine::GROUP[$this->bus_line] . '-' . useScene::SCENEGROUP[$this->scene];
        });
        $grid->column('key_type', 'key类型')->using(redisType::TYPEGROUP);
        $grid->column('db', 'db分块');
        $grid->column('with_id', '共用db')->display(function () {
            if ($this->with_id === 0) {
                return '<div class="span-label" style="margin-bottom: 5px"><span class="label label-default"> 独立存储 </span></div>';
            }
            $iWithId = $this->with_id;

            $iId = $this->id;
            $query = RedisKeySetting::query();
            //查询所有公用的key列表
            $aList = $query->select("b.bus_name", 'b.bus_line', 'b.scene', 'redis_key_setting.id',
                'redis_key_setting.key_name')
                ->where('with_id', $iWithId)
                ->where('redis_key_setting.id', '<>', $iId)
                ->where('is_delete','=',0)
                ->leftJoin("redis_bus_line as b", 'b.id', '=', 'redis_key_setting.b_l_id')
                ->orderBy('redis_key_setting.bus_line', 'desc')
                ->get()->toArray();
            $html = '';
            if (!$aList) {
                return $html;
            }
            foreach ($aList as $list) {
                $html .= '<div class="span-label" style="margin-bottom: 5px"> <span class="label label-info">' . $list['bus_line'] . self::SEP . $list['scene'] . self::SEP . $list['key_name'] . '</span></div>';
            }
            return $html;

        });

        $grid->column('use_time_type', '使用时长')->display(function () {
            if ($this->use_time_type === 1) {
                return '<div class="span-label" style="margin-bottom: 5px"><span class="label label-warning"> 长期使用 </span></div>';
            }
            return '<div class="span-label" style="margin-bottom: 5px"><span class="label label-success">截止：' . $this->end_time . '</span></div>';
        });
        $grid->filter(function ($filter) {
            // 去掉默认的id过滤器
            $filter->disableIdFilter();
            $filter->column(1 / 2, function ($filter) {
                $filter->equal('bus_line', '业务线')->select(businessLine::GROUP);
                $filter->equal('scene', '场景')->select(useScene::SCENEGROUP);
                $filter->equal('use_time_type', '时间类型')->select(RedisKeySetting::USE_TIME_TYPE);
                $filter->equal('end_time', '结束时间')->datetime();

            });

        });


//        $grid->column('counter', '计数器db映射')->display(function () {
//            if ($this->counter) {
//                return $this->counter->table_name . "." . $this->counter->column;
//            }
//        })->label();
        $grid->tools(function (Grid\Tools $tool) {
            $tool->append(new UserButton(UserButton::TYPE_HREF, '', '去发布', 'fa-cloud-upload', url('/admin/Constant/constant-version')));
        });
        $grid->actions(function (Grid\Displayers\Actions $actions) {
            $actions->disableDelete();
            $actions->disableEdit();
            $html_delete = ' <a class="fa edit-pass-data" data-id="' . $actions->row->id . '" href="javascript:void(0);" style="margin-right: 5px">
                                    <span class="glyphicon" aria-hidden="true">删除</span></a>';
            $actions->prepend($html_delete);
        });

        return $grid->render();
    }

    public function create(Content $content)
    {
        $form = new Form(new RedisKeySetting());
        $form->footer(function (Form\Footer $footer) {
            $footer->disableEditingCheck();
            $footer->disableCreatingCheck();
            $footer->disableViewCheck();
        });
        $form->tools(function (Form\Tools $tools) {
            $tools->disableDelete();
            $tools->disableView();
        });

        $aList = RedisBusLine::query()->get()->toArray();
        $aOptions = [];
        foreach ($aList as $item) {
            $aOptions[$item['bus_line'] . self::SEP . $item['scene'] . self::SEP . $item['id']] = $item['bus_line'] . self::SEP . $item['scene'] . self::XHX . $item['bus_name'];
        }
        $form->text('key_name', 'key名称')->setWidth(4,
            2)->required()->help("key名称必须大写是4到32位，允许字母数字下划线组合！");
        $form->text('desc', '描述')->setWidth(4, 2)->required();
        $form->select('bus_line_scene', '使用场景')->options($aOptions)
            ->when("in", RedisKeySetting::COUNTER_KEY_TYPE, function (Form $form) {
                $form->select('db_tables_id', '数据表组')->options(
                    DbTables::all()->pluck('table_name', 'id')
                )->load('column', '/admin/EsStandard/db-tables/getColumns')->setWidth(4, 2);
                $form->select('column', 'db字段')->options(function () {
                    if (!empty($this->counter['db_tables_id'])) {
                        $dbTable = DbTables::find($this->counter['db_tables_id'])->toArray();
                        return DbInstance::getColumnsById($dbTable['db_instance_id'], $dbTable['database'],
                            $dbTable['tables'][0]);
                    }
                    return [];
                })->setWidth(4, 2);
            })->setWidth(4, 2)->required()->help('..CACHE..缓存 场景-仅支持标准的存取（批量存取）方法');
        $form->radio('is_independence', '是否独立db')
            ->options([
                0 => '独立db',
                1 => '与其他key共用db',
            ])->when(1, function (Form $form) {
                $aRedisKeyList = RedisKeySetting::query()
                    ->select("b.bus_name", 'b.bus_line', 'b.scene', 'redis_key_setting.id',
                        'redis_key_setting.key_name')
                    ->leftJoin("redis_bus_line as b", 'b.id', '=', 'redis_key_setting.b_l_id')
                    ->orderBy('redis_key_setting.bus_line', 'desc')
                    ->get()
                    ->toArray();
                $aRedisKeyOptions = [];
                foreach ($aRedisKeyList as $item) {
                    $aRedisKeyOptions[$item['id']] = $item['bus_line'] . self::SEP . $item['scene'] . self::SEP . $item['key_name'] . '-' . $item['bus_name'];
                }
                $form->select('with_id', '选择共用db key')->options($aRedisKeyOptions)->setWidth(4,
                    2)->help("共用db 必选项");
            })->required()->default(0);

        $form->select('key_type', 'key类型')->options(redisType::TYPEGROUP)->setWidth(4, 2)->required()->when(redisType::BITMAPS, function (Form $form) {
            $form->html("<span style='color:red;'>bitmap需要在调用方法中设置过期时间，ttl在此设置没有用，请填写默认0</span>");
        });

        $form->number('ttl', 'ttl')->min(0)->default(0)->setWidth(4,
            2)->required()->help('0表示不需要过期时间（单位秒）');

//        $form->radio('use_time_type', '使用时长')
//            ->options([
//                1 => '长期使用',
//                2 => '临时使用',
//            ])->when(2, function (Form $form) {
//                $form->datetime('end_time', '到期时间')->format('YYYY-MM-DD HH:mm:ss');
//            })->required()->default(1);
        $form->number('use_space', '预估占用空间')->min(0)->default(1)->setWidth(4, 2)->help('（单位 M）');
        $form->setAction('/admin/RedisSetting/redis-key/save');
        return $content
            ->title($this->title)
            ->description()
            ->row($form);
    }

    public function save(Request $request)
    {
        $res = $request->all();
        $bls = explode(self::SEP, $res['bus_line_scene']);
        $redis_bus_line_id = $bls[2];
        if (!$redis_bus_line_id) {
            throw new \Exception('请先添加业务线场景关系');
        }
        unset($bls[2]);
        $res['bus_line_scene'] = implode(self::SEP, $bls);
        try {
            //缓存类型 key类型限定
            if (in_array($res['bus_line_scene'], RedisBusLine::CACHE_SCENE, false)) {
                if (!array_key_exists($res['key_type'], RedisKeySetting::CACHE_KEY_TYPE)) {
                    throw new \Exception('缓存场景仅支持string(推荐)、hash类型');
                }
//                if (($res['ttl'] > 0) && $res['key_type'] == 4) {
//                    throw new \Exception('缓存场景hash类型 ttl必须为0');
//                }
            }

            //计数器类型 key类型限定
            if (in_array($res['bus_line_scene'], RedisBusLine::COUNTER_SCENE,
                    false) && !array_key_exists($res['key_type'], RedisKeySetting::COUNTER_KEY_TYPE)) {
                throw new \Exception('缓存场景仅支持string、zset类型');
            }

            if (($res['is_independence'] === 2) && !$res['with_id']) {
                throw new \Exception('请选择共用db key');
            }

            if (($res['is_independence'] == 1) && !$res['with_id']) {
                throw new \Exception('请选择共用db key！');
            }

            $bIsMatched = preg_match('/^[A-Z0-9_]{4,32}$/', $res['key_name']);
            if (!$bIsMatched) {
                throw new \Exception('key名称必须是大写4到32位，允许字母数字下划线组合！');
            }
            $arr = explode(':', $res['bus_line_scene']);
            $data = [
                'bus_line' => $arr['0'] ?? '',
                'scene' => $arr['1'] ?? '',
                'key_name' => $res['key_name'] ? strtoupper(trim($res['key_name'])) : '',
                'key_type' => $res['key_type'] ?? 0,
                'ttl' => $res['ttl'] ?? 0,
                'use_time_type' => $res['use_time_type'] ?? 1,
                'use_space' => $res['use_space'] ?? 0,
                'with_id' => $res['with_id'] ?: 0,
                'desc' => $res['desc'] ?: '',
            ];
            $data['const_key'] = $data['bus_line'] . "_" . $data['scene'] . "_" . $data['key_name'];
            if ($res['end_time']) {
                $data['end_time'] = $res['end_time'];
            }

            $aWhere['bus_line'] = $data['bus_line'];
            $aWhere['scene'] = $data['scene'];
            $aWhere['key_name'] = $data['key_name'];
            $aWhere['is_delete'] = 0;
            $iRedisKeyId = RedisKeySetting::query()->where($aWhere)->value('id');
            // 判断key名称是否已经存在
            if ($iRedisKeyId) {
                throw new \Exception('键名已存在！');
            }
            //是否选中业务线
            $busLineInfo = RedisBusLine::query()->where(['id' => $redis_bus_line_id])->select('id', 'r_i_id')->first();
            if (!$busLineInfo) {
                throw new \Exception('请先添加业务线场景关系');
            }

            // 实例是否存在
            $instance = RedisInstance::query()->where('id', $busLineInfo['r_i_id'])->first();
            if (!$instance) {
                throw new \Exception('实例不存在');
            }
            $data['b_l_id'] = $busLineInfo['id'];

            if ($res['with_id']) {
                //查询将要绑定的key的 with_id是否是0
                $aInfo = RedisKeySetting::query()->where('id', $res['with_id'])->select('with_id', 'db')->first();
                if (!$aInfo) {
                    throw new \Exception('绑定key不存在！');
                }
                $iBlindWithId = $aInfo['with_id'];
                //绑定的key with_id 不为 0
                if ($iBlindWithId) {
                    $data['db'] = $aInfo['db'];
                    $data['with_id'] = $iBlindWithId;
                } else {
                    //更新绑定的key with_id变为自己的id
                    $aUpData['with_id'] = $res['with_id'];
                    RedisKeySetting::query()->where('id', $aUpData['with_id'])->update($aUpData);
                    $data['db'] = $aInfo['db'];
                }

            } else {
                //分配db
                $data['db'] = $this->getRedisDb($busLineInfo['r_i_id']);
                $aLogData['db'] = $data['db'];
                $aLogData['instance_id'] = $busLineInfo['r_i_id'];
                $aLogData['status'] = RedisDbLog::STATUS_NORMAL;
                RedisDbLog::addData($aLogData);
            }


            // 添加
            $returnId = RedisKeySetting::query()->insertGetId($data);
            if (!empty($res['db_tables_id']) && !empty($res['column'])) {
                RedisCounterColumn::query()->insert([
                    'key_id' => $returnId,
                    'db_tables_id' => $res['db_tables_id'],
                    'column' => $res['column']
                ]);
            }
            $dataId = $data['bus_line'] . self::SEP . $data['scene'] . self::SEP . $data['key_name'] . '.yaml';
            $configData[Constants::ENV_DEV] = [
                'host' => $instance['dev_host'],
                'port' => $instance['dev_port'],
                'user' => $instance['dev_user'],
                'password' => $instance['dev_password'],
                'db' => 0,
            ];
            $configData[Constants::ENV_TEST] = [
                'host' => $instance['test_host'],
                'port' => $instance['test_port'],
                'user' => $instance['test_user'],
                'password' => $instance['test_password'],
                'db' => 0,
            ];
            if (in_array(HhzEnv::getAppEnv(), [HhzEnv::ENV_PRODUCT, HhzEnv::ENV_GRAY])) {
                $configData[Constants::ENV_PRODUCT] = [
                    'host' => $instance['host'],
                    'port' => $instance['port'],
                    'user' => $instance['user'],
                    'password' => $instance['password'],
                    'db' => $data['db'],
                ];
                $configData[Constants::ENV_GRAY] = [
                    'host' => $instance['host'],
                    'port' => $instance['port'],
                    'user' => $instance['user'],
                    'password' => $instance['password'],
                    'db' => $data['db'],
                ];
            }
            ConfigServices::BatchPublishConfig($dataId, 'redis', $configData);

            $success = new MessageBag([
                'title' => '成功',
            ]);

        } catch (\Exception $e) {
            $error = new MessageBag([
                'title' => $e->getMessage(),
            ]);
            return back()->with(compact('error'))->withInput();

        }
        return redirect('/admin/RedisSetting/redis-key')->with(compact('success'));
    }

    public function delete(Request $request)
    {
        $iId = $request->post('id');
        try {
            $info = RedisKeySetting::query()->where('id', $iId)->where('is_delete', 0)->first();
            if (!$info) {
                throw new \Exception('该条内容不存在！');
            }
            $db = $info['db'];
            $iNum = RedisKeySetting::query()->where('db', $db)->where('is_delete', 0)->count();
            if ($iNum <= 1) {
                RedisDbLog::query()->where('db', $db)->delete();
            }
            RedisKeySetting::query()->where('id', $iId)->update(['is_delete' => $iId]);
            $success = [
                'title' => '操作成功',
                'code' => 1,
            ];
        } catch (\Exception $e) {
            $success = [
                'title' => '操作失败,' . $e->getMessage(),
                'msg' => '信息：' . $e->getFile() . $e->getLine(),
                'code' => 0,
                'status' => false,
            ];
        }

        return response()->json($success);


    }


    /**
     * 获取db
     *
     * @return int|void
     * @time 2022/12/30 17:22
     * @desc
     */
    public function getRedisDb($iInstanceId)
    {
        $aDbList = RedisDbLog::getDbList($iInstanceId);
        for ($i = 0; $i <= 255; $i++) {
            if (in_array($i, $aDbList, false)) {
                continue;
            }
            return $i;
        }
    }


    private function deleteScript()
    {
        return <<<SCRIPT
        $('.edit-pass-data').unbind('click').click(function(){
            var id= $(this).data('id');
            let url = '/admin/RedisSetting/redis-key/delete'
            let params = {id:id}
            swal({
                    title: "确认删除吗？",
                    type: "warning",
                    showCancelButton: true,
                    confirmButtonColor: "#DD6B55",
                    confirmButtonText: "确认",
                    showLoaderOnConfirm: true,
                    cancelButtonText: "取消",
                    preConfirm: function() {
                    //ajax
                        $.ajax({
                            method: 'post',
                            url: url,
                            data: params,
                            success: function (e) {
                                if (e.code == 1) {
                                swal({
                                    title: e.title,
                                    type: "warning",
                                });
                                    setTimeout(function () {
                                        $.pjax.reload('#pjax-container');
                                    }, 1000);
                                } else {
                                    toastr.error(e.title, null, {
                                        timeOut : 3000
                                    }); 
                                }
                            }
                        });
                    //ajax-end
                    }
            });
        });
       
SCRIPT;
    }

}
